package bg.btanev.eightyfour.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bg.btanev.eightyfour.dao.PagingConfig;
import bg.btanev.eightyfour.dao.PagingConfig.OrderConfig;
import bg.btanev.eightyfour.domain.Tag;
import bg.btanev.eightyfour.domain.Target;
import bg.btanev.eightyfour.domain.profile.Profile;
import bg.btanev.eightyfour.domain.profile.ProfileEvent;
import bg.btanev.eightyfour.domain.profile.feed.FeedProfile;
import bg.btanev.eightyfour.repository.ProfileRepository;
import bg.btanev.eightyfour.repository.TagRepository;
import bg.btanev.eightyfour.repository.TargetRepository;
import bg.btanev.eightyfour.service.ProfileService;
import bg.btanev.eightyfour.shared.state.ProfileType;

/**
 *
 * @author B.Tanev
 *
 */
public class ProfileServiceImpl implements ProfileService {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(ProfileServiceImpl.class);

	private ProfileRepository profileRepository;
	private TagRepository tagRepository;
	private TargetRepository targetRepository;

	public ProfileServiceImpl() {
	}

	public void setProfileRepository(ProfileRepository profileRepository) {
		this.profileRepository = profileRepository;
	}

	public void setTargetRepository(TargetRepository targetRepository) {
		this.targetRepository = targetRepository;
	}

	public void setTagRepository(TagRepository tagRepository) {
		this.tagRepository = tagRepository;
	}

	@Override
	public List<Profile<ProfileEvent>> findAllProfiles(Target target) {
		return profileRepository.findAllProfiles(target);
	}

	@Override
	public List<ProfileEvent> findProfileEventsForTarget(Target target) {
		PagingConfig pagingConfig = new PagingConfig(-1, -1);
		pagingConfig.addOrder(new OrderConfig("date", true));

		return profileRepository.findProfileEventsForTarget(target, pagingConfig);
	}

	@Override
	public List<ProfileEvent> findProfileEvents(Target target, Date beginDate, Date endDate, ProfileType profileType, String searchQuery,
			List<String> tagNames) {

		logger.debug("findProfileEvents(Target, Date, Date, String, List<String>) - start"); //$NON-NLS-1$

		List<Tag> tags = extractTags(tagNames);
		Set<String> keywords = extractKeywords(searchQuery);

		PagingConfig pagingConfig = new PagingConfig(-1, -1);
		pagingConfig.addOrder(new OrderConfig("date", true));

		List<ProfileEvent> returnList = profileRepository.findProfileEventsForTarget(target, beginDate, endDate, profileType, tags,
				keywords, pagingConfig);

		logger.debug("findProfileEvents(Target, Date, Date, String, List<String>) - end"); //$NON-NLS-1$
		return returnList;
	}

	private Set<String> extractKeywords(String searchQuery) {
		Set<String> keywords = null;

		if (searchQuery != null) {
			searchQuery = searchQuery.trim();
			if (searchQuery.length() > 0) {
				keywords = new LinkedHashSet<String>();

				doExtractSearchExpresions(keywords, searchQuery);

				logger.info("extractKeywords(String) - '{}' search expresions extracted", keywords.size()); //$NON-NLS-1$
			}
		}

		return keywords;
	}

	private void doExtractSearchExpresions(Set<String> resultCollection, String searchQuery) {
		assert (resultCollection != null);
		assert (searchQuery != null && searchQuery.length() > 0);

		String[] expressionSplit = searchQuery.split("\""); // splitting by "
		for (int i = 0; i < expressionSplit.length; i++) {
			String expression = expressionSplit[i];
			if ((i % 2) != 0) {
				// odd - exact expression math
				resultCollection.add(expression);
			} else {
				// even - extract keywords only
				expression = expression.trim().toLowerCase();

				// extracting words - trimming all whitespaces
				String[] words = expression.split("[\\s]+");
				for (String word : words) {
					if (word.length() < 3) {
						// if the current 'word' has only letters and is shorter that 3 characters - ignore it
						boolean hasOnlyLetters = true;
						for (int c = 0; c < word.length(); c++) {
							if (!(Character.isLetter(word.charAt(c)))) {
								hasOnlyLetters = false;
								break;
							}
						}
						if (hasOnlyLetters) {
							continue;
						}
					}

					resultCollection.add(word);
				}
			}
		}
	}

	private List<Tag> extractTags(List<String> tagNames) {
		List<Tag> result = null;

		if (tagNames != null && tagNames.size() > 0) {
			result = new ArrayList<Tag>(tagNames.size());
			for (String name : tagNames) {
				result.add(tagRepository.getTag(name));
			}

			logger.info("extractTags(List<String>) - '{}' tags extracted", result.size()); //$NON-NLS-1$
		}

		return result;
	}

	@Override
	public void deleteProfile(Long identifier) {
		logger.debug("deleteProfile(Long) - start"); //$NON-NLS-1$

		Profile<ProfileEvent> profile = profileRepository.findByIdentifier(identifier);
		if (profile != null) {
			Target target = profile.getTarget();
			target.removeProfile(profile);
			targetRepository.storeTarget(target);
//			profileRepository.removeProfile(profile);
		} else {
			logger.debug("deleteProfile(Long) - No profile found - no delete"); //$NON-NLS-1$
		}

		logger.debug("deleteProfile(Long) - end"); //$NON-NLS-1$
	}

	@Override
	public FeedProfile createAndStoreFeedProfile(String name, ProfileType type, Target target, String feedUrl) {
		logger.debug("createAndStoreFeedProfile(String, ProfileType, Target, String) - start"); //$NON-NLS-1$

		Validate.notNull(target);

		Target reassociateTarget = targetRepository.reassociateTarget(target);
		if (reassociateTarget == null) {
			reassociateTarget = targetRepository.findByIdentifier(target.getId());
		}

		FeedProfile newProfile = null;
		if (reassociateTarget != null) {
			newProfile = profileRepository.createAndStoreFeedProfile(name, type, reassociateTarget, feedUrl);
		} else {
			logger
					.info("createAndStoreFeedProfile(String, ProfileType, Target, String) - Count not reassiciate target. Maybe it does not exists?! Id: " + target.getId()); //$NON-NLS-1$
		}
		logger
				.info("createAndStoreFeedProfile(String, ProfileType, Target, String) - FeedProfile newProfile=" + newProfile); //$NON-NLS-1$

		logger.debug("createAndStoreFeedProfile(String, ProfileType, Target, String) - end"); //$NON-NLS-1$
		return newProfile;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Profile<ProfileEvent> updateFeedProfile(Long identifier, String name, String feedUrl) {
		Profile<? extends ProfileEvent> profile = profileRepository.findByIdentifier(identifier);
		if (profile != null) {
			profile.setName(name);
			if (profile instanceof FeedProfile) {
				((FeedProfile) profile).setFeedUrl(feedUrl);
			}
			profileRepository.storeProfile((Profile<ProfileEvent>) profile);
		} else {
			logger
					.debug(
							"updateFeedProfile(Long, String, String) - No profile found for id '{}' cannot update profile", identifier); //$NON-NLS-1$
		}

		return (Profile<ProfileEvent>) profile;
	}
}
